﻿using System;
using System.Collections.Generic;
using System.IO;

namespace InfFront.UICore
{
    /// <summary>
    /// IDataType helper interface
    /// </summary>
    public interface IDataTypeTraits
    {
        Type Type { get; }

        object ReadData(BinaryReader reader);
        void WriteData(object data, BinaryWriter writer);
    }

    public class SimpleSigleton<T>
        where T: new()
    {
        private static readonly T instance;

        static SimpleSigleton()
        {
            instance = new T();
        }

        public static T Instance
        {
            get { return instance; }
        }
    }

    /// <summary>
    /// String data traits
    /// </summary>
    public class StringDataTraits : SimpleSigleton<StringDataTraits>, IDataTypeTraits
    {
        public Type Type
        {
            get { return typeof(string); }
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            writer.Write((data == null)? string.Empty: data.ToString());
        }

        public object ReadData(BinaryReader reader)
        {
            return reader.ReadString();
        }
    }

    /// <summary>
    /// Int data traits
    /// </summary>
    public class IntDataTraits : SimpleSigleton<IntDataTraits>, IDataTypeTraits
    {
        public Type Type
        {
            get { return typeof(Int32); }
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            writer.Write((Int32)data);
        }

        public object ReadData(BinaryReader reader)
        {
            return reader.ReadInt32();
        }
    }

    /// <summary>
    /// Long int data traits
    /// </summary>
    public class LongDataTraits : SimpleSigleton<LongDataTraits>, IDataTypeTraits
    {
        public Type Type
        {
            get { return typeof(Int64); }
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            writer.Write((Int64)data);
        }

        public object ReadData(BinaryReader reader)
        {
            return reader.ReadInt64();
        }
    }

    /// <summary>
    /// Bool data traits
    /// </summary>
    public class BoolDataTraits : SimpleSigleton<BoolDataTraits>, IDataTypeTraits
    {
        public Type Type
        {
            get { return typeof(bool); }
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            writer.Write((bool)data);
        }

        public object ReadData(BinaryReader reader)
        {
            return reader.ReadBoolean();
        }
    }

    /// <summary>
    /// Double data traits
    /// </summary>
    public class DoubleDataTraits : SimpleSigleton<DoubleDataTraits>, IDataTypeTraits
    {
        public Type Type
        {
            get { return typeof(double); }
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            writer.Write((double)data);
        }

        public object ReadData(BinaryReader reader)
        {
            return reader.ReadDouble();
        }
    }

    /// <summary>
    /// DateTime data traits
    /// </summary>
    public class DateTimeDataTraits : SimpleSigleton<DateTimeDataTraits>, IDataTypeTraits
    {
        #region IDataTypeTraits Members

        public Type Type
        {
            get { return typeof(DateTime); }
        }

        public object ReadData(BinaryReader reader)
        {
            var asLong = reader.ReadInt64();
            if (asLong == 0)
                return null;
            return DateTime.FromBinary(asLong);
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            if (data == null)
                writer.Write((long)0);
            else
            {
                var dateTime = (DateTime)data;
                writer.Write(dateTime.ToBinary());
            }
        }

        #endregion
    }

    /// <summary>
    /// Bool data traits
    /// </summary>
    public class ImageDataTraits : SimpleSigleton<ImageDataTraits>, IDataTypeTraits
    {
        public Type Type
        {
            get { return typeof(IImage); }
        }

        public void WriteData(object data, BinaryWriter writer)
        {
            byte[] buf = data as byte[];
            if (buf == null)
            {
                Stream stream = data as Stream;
                if (stream == null)
                {
                    var asIImage = data as IImage;
                    stream = (asIImage == null) ? null : asIImage.GetData();
                    if (stream == null)
                    {
                        writer.Write((Int32)0);
                        return;
                    }
                }
                if (!stream.CanSeek)
                    throw new NotSupportedException("Image streams must support seeking");
                if (stream.Length > Int32.MaxValue)
                    throw new NotSupportedException(string.Format("Image stream has too big size: {0}", stream.Length));
                buf = new byte[stream.Length];
                stream.Read(buf, 0, buf.Length);
            }
            writer.Write((Int32)buf.Length);
            if (buf.Length > 0)
                writer.Write(buf);
        }

        public object ReadData(BinaryReader reader)
        {
            return new DummyImage(reader);
        }

        public class DummyImage : IImage
        {
            private Stream data = null;
            private string uid = null;

            public DummyImage(Stream data, string uid)
            {
                this.data = data;
                this.uid = uid;
            }

            public DummyImage(BinaryReader reader)
            {
                Int32 size = reader.ReadInt32();
                if (size < 0)
                    throw new IndexOutOfRangeException(string.Format("Wrong image buffer size: {0}", size));
                if (size > 0)
                    data = new MemoryStream(reader.ReadBytes(size));
            }

            #region IImage Members

            public string GetUid()
            {
                return uid;
            }

            public Stream GetData()
            {
                return data;
            }

            #endregion
        }
    }

    /// <summary>
    /// Dummy data traits
    /// </summary>
    public class DummyDataTraits : SimpleSigleton<DummyDataTraits>, IDataTypeTraits
    {
        #region IDataTypeTraits Members

        public Type Type
        {
            get { return typeof(object); }
        }

        public object ReadData(BinaryReader reader)
        {
            return null;
        }

        public void WriteData(object data, BinaryWriter writer)
        {
        }

        #endregion
    }

    /// <summary>
    /// Default data type utility procedures
    /// </summary>
    public static class DataTraits
    {
        /// <summary>
        /// guess the IDataTypeTraits for an IDataType
        /// </summary>
        /// <param name="desc">descriptor</param>
        /// <returns></returns>
        public static IDataTypeTraits GetTraits(this IDataType desc)
        {
            if (null != (desc as IStringDataDesc))
                return StringDataTraits.Instance;
            if (null != (desc as IIntDataDesc))
                return IntDataTraits.Instance;
            if (null != (desc as ILongDataDesc))
                return LongDataTraits.Instance;
            if (null != (desc as IBoolDataDesc))
                return BoolDataTraits.Instance;
            if (null != (desc as IFloatDataDesc))
                return DoubleDataTraits.Instance;
            if (null != (desc as IDateTimeDataDesc))
                return DateTimeDataTraits.Instance;
            if (null != (desc as IImageDataDesc))
                return ImageDataTraits.Instance;
            return DummyDataTraits.Instance;
        }
    }
}
